package com.ruoyi.pingan.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

@Configuration
public class TaskExecutePoolConfig {
    /**
     * 实现多线程的方式
     * 1）继承Thread
     * 2）实现Runnable接口
     * 3）实现Callable接口+FutureTask
     * 4）线程池提交任务
     * 实际开发中。应该蒋所有的多线程异步任务都交给线程池处理。资源控制,性能稳定。
     * <p>
     * 1.如何创建线程池[ExecutorService]
     * 1). Executors.newFixedThreadPool()
     * 2). new ThreadPoolExecutor 七大参数
     * int corePoolSize: 【10】核心线程数。线程池创建好以后就准备就绪的线程数量。等待来接受异步任务去执行。只要线程池不销毁，核心线程数一直存在。
     * int maximumPoolSize: 【200】 最大线程数量。控制资源。
     * long keepAliveTime: 存活时间。如果当前的线程数量大于核心线程数量。释放空闲线程。只要线程空闲大于指定的空闲时间keepAliveTime。释放的线程指的是最大的大小减去核心线程数量。
     * TimeUnit unit: 存活时间的单位。
     * BlockingQueue<Runnable> workQueue: 阻塞队列。如果任务有很多（超过了最大线程数），就会将目前多的任务放在队列中。只要有空闲的线程，就会去队列中取出新的任务执行。
     * ThreadFactory threadFactory: 线程的创建工厂。一般都是默认的。具体是创建线程，例如可以自定义线程的名称等。
     * RejectedExecutionHandler handler: 如果队列workQueue满了，按照指定的拒绝策略，拒绝执行任务。
     * 3).运行流程
     * 1.线程池创建，准备好core数量的核心线程，准备接收任务。
     * 2.core满了，就将再进来的任务放入阻塞队列中。空闲的core就会自己去阻塞队列获取任务执行。
     * 3.阻塞队列也满了，就会直接开新的线程去执行，最大只能开到max指定的数量。
     * 4.max满了后，就用RejectedExecutionHandler拒绝策略
     * 5.max执行完成后，有很多空闲的线程，在指定的时间keepAliveTime以后，还是空闲的话，就释放max减去core这些线程。
     * 2.案例： 一个线程池core=7,max=20,queue=50,100个并发怎么分配
     * 1).7个任务会立刻执行
     * 1).50个任务会放入到队列中。
     * 1).新开13个线程去执行。
     * 1).剩下的30个使用拒绝策略执行，一般都是丢弃。
     */
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Bean("threadPoolExecutor")
    public ThreadPoolExecutor getAsyncExecutor() {
        int corePollSize = 80;
        int maxPollSize = 120;
        long keepAliveTime = 300;
        int queueSize = 100;
        logger.info("corePoolSize:{}----maxPoolSize:{}----keepAliveTime:{}----QueueSize:{}",
                corePollSize, maxPollSize, keepAliveTime, queueSize);
        return new ThreadPoolExecutor(
                corePollSize,
                maxPollSize,
                keepAliveTime,
                TimeUnit.MINUTES,
                new LinkedBlockingDeque<>(queueSize),
                new CustomThreadFactory(),
                new CustomRejectedExecutionHandler());
    }

    private class CustomThreadFactory implements ThreadFactory {

        private AtomicInteger count = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            String threadName = TaskExecutePoolConfig.class.getSimpleName() +
                    "----" + count.addAndGet(1);
            logger.info("thread name is：{}", threadName);
            t.setName(threadName);
            return t;
        }
    }

    private class CustomRejectedExecutionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            try {
                executor.getQueue().put(r);
                logger.info("we will retry our task...");
            } catch (InterruptedException e) {
                logger.error(e.getMessage());
                Thread.currentThread().interrupt();
            } catch (Exception e) {
                logger.error("we will retry our task...",e);
            }
        }
    }


}
